using System.Runtime.CompilerServices;
using System.Collections.Generic;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.transaction
{

	using XAException = javax.transaction.xa.XAException;
	using XAResource = javax.transaction.xa.XAResource;

	using XaDataSource = org.neo4j.kernel.impl.transaction.xaframework.XaDataSource;
	using XaResource = org.neo4j.kernel.impl.transaction.xaframework.XaResource;

///
/// <summary> * All datasources that have been defined in the XA data source configuration
/// * file or manually added will be created and registered here. A mapping between
/// * "name", "data source" and "branch id" is kept by this manager.
/// * <p>
/// * Use the <seealso cref="#getXaDataSource"/> to obtain the instance of a datasource that
/// * has been defined in the XA data source configuration.
/// *  </summary>
/// * <seealso cref= XaDataSource </seealso>
/// 
	public class XaDataSourceManager
	{
	// key = data source name, value = data source
		private readonly Map<string, XaDataSource> dataSources = new Dictionary<string, XaDataSource>();
	// key = branchId, value = data source
		private readonly Map<string, XaDataSource> branchIdMapping = new Dictionary<string, XaDataSource>();
	// key = data source name, value = branchId
		private readonly Map<string, sbyte[]> sourceIdMapping = new Dictionary<string, sbyte[]>();

		internal XaDataSourceManager()
		{
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: XaDataSource create(String className, Map<?,?> params) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException
		internal virtual XaDataSource create<T1>(string className, Map<T1> @params)
		{
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Class<?> clazz = Class.forName(className);
			System.Type<?> clazz = System.Type.GetType(className);
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Constructor<?>[] constructors = clazz.getConstructors();
			Constructor<?>[] constructors = clazz.getConstructors();
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: for (Constructor<?> constructor : constructors)
			foreach (Constructor<?> constructor in constructors)
			{
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Class<?>[] parameters = constructor.getParameterTypes();
				System.Type<?>[] parameters = constructor.getParameterTypes();
				if (parameters.Length == 1 && parameters[0].Equals(typeof(Map)))
				{
					return (XaDataSource) constructor.newInstance(new object[] { @params });
				}
			}
			throw new InstantiationException("Unable to instantiate " + className + ", no valid constructor found.");
		}

///    
///     <summary> * Convenience method since <seealso cref="#getXaDataSource"/> returns null if name
///     * doesn't exist.
///     *  </summary>
///     * <returns> True if name exists </returns>
///     
		public virtual bool hasDataSource(string name)
		{
			return dataSources.containsKey(name);
		}

///    
///     <summary> * Returns the <seealso cref="org.neo4j.kernel.impl.transaction.xaframework.XaDataSource"/>
///     * registered as <CODE>name</CODE>. If no data source is registered with
///     * that name <CODE>null</CODE> is returned.
///     *  </summary>
///     * <param name="name">
///     *            the name of the data source </param>
///     
		public virtual XaDataSource getXaDataSource(string name)
		{
			return dataSources.get(name);
		}

///    
///     <summary> * Public for testing purpose. Do not use. </summary>
///     
		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void registerDataSource(string name, XaDataSource dataSource, sbyte[] branchId)
		{
			dataSource.setBranchId(branchId);
			dataSource.Name = name;
			dataSources.put(name, dataSource);
			branchIdMapping.put(new string(branchId), dataSource);
			sourceIdMapping.put(name, branchId);
		}

///    
///     <summary> * Public for testing purpose. Do not use. </summary>
///     
		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void unregisterDataSource(string name)
		{
			XaDataSource dataSource = dataSources.get(name);
			sbyte[] branchId = getBranchId(dataSource.getXaConnection().getXaResource());
			dataSources.Remove(name);
			branchIdMapping.Remove(new string(branchId));
			sourceIdMapping.Remove(name);
			dataSource.Close();
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void unregisterAllDataSources()
		{
			branchIdMapping.Clear();
			sourceIdMapping.Clear();
			IEnumerator <XaDataSource> itr = dataSources.values().GetEnumerator();
			while (itr.MoveNext())
			{
				XaDataSource dataSource = itr.Current;
				dataSource.Close();
			}
			dataSources.Clear();
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual sbyte[] getBranchId(XAResource xaResource)
		{
			if (xaResource is XaResource)
			{
				sbyte[] branchId = ((XaResource) xaResource).getBranchId();
				if (branchId != null)
				{
					return branchId;
				}
			}
			IEnumerator <Map.Entry<string, XaDataSource>> itr = dataSources.entrySet().GetEnumerator();
			while (itr.MoveNext())
			{
				Map.Entry<string, XaDataSource> entry = itr.Current;
				XaDataSource dataSource = entry.Value;
				XAResource resource = dataSource.getXaConnection().getXaResource();
				try
				{
					if (resource.isSameRM(xaResource))
					{
						string name = entry.Key;
						return sourceIdMapping.get(name);
					}
				}
				catch (XAException e)
				{
					throw new TransactionFailureException("Unable to check is same resource", e);
				}
			}
			throw new TransactionFailureException("Unable to find mapping for XAResource[" + xaResource + "]");
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual XAResource getXaResource(sbyte[] branchId)
		{
			XaDataSource dataSource = branchIdMapping.get(new string(branchId));
			if (dataSource == null)
			{
				throw new TransactionFailureException("No mapping found for branchId[0x" + new string(branchId) + "]");
			}
			return dataSource.getXaConnection().getXaResource();
		}

	// not thread safe
		public virtual IEnumerable <XaDataSource> getAllRegisteredDataSources()
		{
			return dataSources.values();
		}
	}
}